package me.wpf.DesignMode.singleton;

/**
 * Description:懒汉式，使用时才会产生内存开销
 *
 * @author 王朋飞
 * @create 2019/5/5
 */
public class Singleton1 {
    public static Singleton1 instance;
    public static volatile Singleton1 instance1;

    private Singleton1() {
    }

    /**
     * 线程不安全
     *
     * @return
     */
    public static Singleton1 getInstance() {
        if (instance == null) {
            instance = new Singleton1();
        }
        return instance;
    }

    /**
     * 线程安全，能保证只得到一个对象
     * 缺点：在高并发情况下，可能线程阻塞到getInstance1()外，造成线程等待，效率低
     *
     * @return
     */
    public static synchronized Singleton1 getInstance1() {
        if (instance == null) {
            instance = new Singleton1();
        }
        return instance;
    }

    /**
     * 线程不安全
     * getInstance2是getInstance1的优化，相对减少了锁开销，不能保证只得到一个对象
     * 缺点：可能造成创建多个对象
     * 例如：step1: 线程1、2运行到//1处，这时instance=null;
     * step2: 线程1进入锁内部，线程2等待线程1释放锁；线程1创建一个对象，释放锁，唤醒线程2
     * step3: 线程2持有锁，又创建一个对象
     *
     * @return
     */
    public static Singleton1 getInstance2() {
        if (instance == null) {//1
            synchronized (Singleton1.class) {//2
                instance = new Singleton1();
            }
        }
        return instance;
    }

    /**
     * 线程不安全
     * getInstance3是getInstance2的优化，采用双层校验锁机制，减少了锁开销，不能保证只得到一个对象
     * 缺点：看似完美，但可能造成创建多个对象，原因出在了instance = new Singleton1()，这个操作jvm做了三件事：
     *       1.在堆中分配对象所需的内存；
     *       2.将堆区对象的内存地址赋给栈区的引用变量instance；
     *       3.然后执行new,初始化对象在堆内存地址中的数据；
     *      步骤3是较消耗时间的，且jvm对这三个操作是无序的，所以存在当线程返回一个对象时，这个对象可能并没有构造完成, 这时instance=null;
     *
     * @return
     */
    public static Singleton1 getInstance3() {
        if (instance == null) {//1
            synchronized (Singleton1.class) {//2
                if (instance == null) {//3
                    instance = new Singleton1();//4
                }
            }
        }
        return instance;
    }

    /**
     * 线程安全
     * getInstance4是getInstance3的优化，采用volatile关键字修饰变量，而又不会对性能造成太大的影响。
     * 它只是第一次创建实例的时候同步，以后就不需要同步了，从而加快了运行速度。
     *
     * Volatile 变量具有 synchronized 的可见性特性，但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。
     * @return
     */
    public static Singleton1 getInstance4() {
        if (instance1 == null) {//1
            synchronized (Singleton1.class) {//2
                if (instance1 == null) {//3
                    instance1 = new Singleton1();//4
                }
            }
        }
        return instance1;
    }

}
